/* test_aig2.c */
#include <stdio.h>
#include <pthread.h>
#include "semaphore.h"

void departManuel();
void verificationDesCompteurs();
void initialisation();
void liberationRessources();

const char * getStringFromType(int i){    
	if( i == 0) return "TGV";
	if( i == 1) return "GL";
	if( i == 2) return "M";  
}
char tabulation[] = "\t \t \t";
const char * getTabulationFromSens(int i){    
	if( i == 1) return tabulation;
	if( i == 0) return ""; 
}

typedef enum {TGV, GL, M} Type;
typedef enum {ouestEst,estOuest} Sens;
typedef struct Train Train;
struct Train{
	Type type;
	Sens sens;
	int num;
	pthread_t thread;
};
int i,j,k,l,m,n;
//Temps de passage des différentes sections
int tpsLigne = 2;
int tpsTunnel = 2;
int tpsAig1 = 15;
int tpsAig2 = 3;
int tpsGare = 3;
int tpsGarage = 3;

//Temps entre la mise sur rails des trains
int tpsEntreTrains = 1;

//intialisation 
int nbTrainAttVoieA=0;
int nbTrainVoieC=0;
int nbTrainAig1_OE=0;
int nbTrainAig1_EO=0;
int nbTrainAig2_OE=0;
int nbTrainAig2_EO=0;
int nbTgvVoieGarage_OE=0;
int nbTgvVoieGarage_EO=0;
int nbGlVoieGarage_OE=0;
int nbGlVoieGarage_EO=0;
int nbMVoieGarage_OE=0;
int nbMVoieGarage_EO=0;
int nbTrainTunnel_OE=0;
int nbTrainTunnel_EO=0;
int nbTrainAttTunnel=0;
int nbTgvTotal_OE=0;
int nbTgvTotal_EO=0;
int nbGlTotal_OE=0;
int nbGlTotal_EO=0;
int nbTrainLigne_EO=0;
int nbTrainLigne_OE=0;
int nbTgvAttTunnel=0;
int nbGlAttTunnel=0;
int nbMAttTunnel=0;
int nbTgvAttSensVoieC=0;
int nbTgvAttSensLigne=0;
int nbTrainAttSensLigneLigne=0;
int nbTrainAttSensLigneVoieGarage=0;
int nbGlAttSensVoieC=0;
int nbGlAttSensLigne=0;


//Les Semaphores
Semaphore semTGV,semSensTGV,semGL,semSensGL,semLigne,semSensLigne;

//Les Mutex
pthread_mutex_t mtxVoieA,mtxAig1, mtxAig2, mtxTunnel;

//Les conditions
pthread_cond_t 	condTrainAttAig2,condTrainAttLigne,condTrainAttVoieA,
		condTgvAttTunnel,condTgvAttAig2,
		condGlAttTunnel,condGlAttAig2,
		condMAttTunnel,condMAttAig1,
		condDormirAig1,condDormirAig2,condDormirTunnel;


/******************************************************************************************/
/*****************************>>  Fonction gestion du Aig1  <<*****************************/
/******************************************************************************************/
void aig1(){
	pthread_mutex_lock(&mtxAig1);
		if(nbTrainAttVoieA>0){
			pthread_cond_signal(&condTrainAttVoieA);
		}else if (nbMVoieGarage_EO>0){
			pthread_cond_signal(&condMAttAig1);
		}else{
			pthread_cond_wait(&condDormirAig1,&mtxAig1);
		}
	pthread_mutex_unlock(&mtxAig1);
}
/******************************************************************************************/
/*****************************>>  Fonction gestion du Aig2  <<*****************************/
/******************************************************************************************/
void aig2(){
	pthread_mutex_lock(&mtxAig2);
		if(nbTgvVoieGarage_EO>0){
			pthread_cond_signal(&condTgvAttAig2);
		}else if (nbGlVoieGarage_EO>0){
			pthread_cond_signal(&condGlAttAig2);
		}else if (nbTrainVoieC>0){
			pthread_cond_signal(&condTrainAttAig2);
		}else{
			pthread_cond_wait(&condDormirAig2,&mtxAig2);
		}
	pthread_mutex_unlock(&mtxAig2);
}
/********************************************************************************************/
/*****************************>>  Fonction gestion du Tunnel  <<*****************************/
/********************************************************************************************/
void tunnel(){
	pthread_mutex_lock(&mtxTunnel);
	if(nbTgvAttTunnel>0){
		pthread_cond_signal(&condTgvAttTunnel);
	}else if (nbGlAttTunnel>0){
		pthread_cond_signal(&condGlAttTunnel);
	}else if (nbTrainAttTunnel>0){
		pthread_cond_signal(&condTrainAttLigne);
	}else if (nbMAttTunnel>0){
		pthread_cond_signal(&condMAttTunnel);
	}else {	
		pthread_cond_wait(&condDormirTunnel,&mtxTunnel);
	}
	pthread_mutex_unlock(&mtxTunnel);
}

/******************************************************************************************/
/*****************************>>  Fonction passage du Aig1  <<*****************************/
/******************************************************************************************/
void passage_Aig1(Train* train){
	pthread_mutex_lock(&mtxAig1);
		pthread_cond_signal(&condDormirAig1);
		printf("%sLe %s %d avertit aig1 qu'il souhaite passer\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
		if(train->sens==ouestEst){
			pthread_cond_wait(&condTrainAttVoieA,&mtxAig1); // attend qu'on lui dise de passe
			nbTrainAttVoieA--;

			nbTrainAig1_OE++;
		}else if(train->sens==estOuest){
		
			pthread_cond_wait(&condMAttAig1,&mtxAig1);
			nbMVoieGarage_EO--;
			nbTrainAig1_EO++;
		}
		printf("%sLe %s %d entre dans aig1\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
		sleep(tpsAig1);
		printf("%sLe %s %d sort de aig1\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
	pthread_mutex_unlock(&mtxAig1);
}	

/******************************************************************************************/
/*****************************>>  Fonction passage du aig2  <<*****************************/
/******************************************************************************************/
void passage_Aig2(Train* train){
	pthread_mutex_lock(&mtxAig2);
		pthread_cond_signal(&condDormirAig2);
		printf("%sLe %s %d avertit aig2 qu'il souhaite passer\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
		
		if(train->sens==estOuest && train->type==TGV){
			pthread_cond_wait(&condTgvAttAig2,&mtxAig2);
			nbTgvVoieGarage_EO--;
			nbTrainAig2_EO++;
		}else if(train->sens==ouestEst){
			pthread_cond_wait(&condTrainAttAig2,&mtxAig2); // attend qu'on lui dise de passe
			nbTrainVoieC--;
			nbTrainAig2_OE++;
		}
		else if(train->sens==estOuest && train->type==GL){
			pthread_cond_wait(&condGlAttAig2,&mtxAig2);
			nbGlVoieGarage_EO--;
			nbTrainAig2_EO++;
		}
		printf("%sLe %s %d entre dans aig2\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
		sleep(tpsAig2);
		printf("%sLe %s %d sort de aig2\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
	pthread_mutex_unlock(&mtxAig2);
}

/********************************************************************************************/
/*****************************>>  Fonction passage du tunnel  <<*****************************/
/********************************************************************************************/
void passage_Tunnel(Train* train){
	pthread_mutex_lock(&mtxTunnel);
		pthread_cond_signal(&condDormirTunnel);
		printf("%sLe %s %d avertit le tunnel qu'il souhaite passer\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
		if(train->sens==estOuest){
			pthread_cond_wait(&condTrainAttLigne,&mtxTunnel); // attend qu'on lui dise de passe
			nbTrainAttTunnel--;
		}
		else if(train->sens==ouestEst && train->type==TGV){
			nbTgvAttTunnel++;
			pthread_cond_wait(&condTgvAttTunnel,&mtxTunnel);
			nbTgvAttTunnel--;
			nbTgvVoieGarage_OE--;
			nbTrainTunnel_OE++;
		}else if(train->sens==ouestEst && train->type==GL){
			nbGlAttTunnel++;
			pthread_cond_wait(&condGlAttTunnel,&mtxTunnel);
			nbGlAttTunnel--;
			nbGlVoieGarage_OE--;
			nbTrainTunnel_OE++;
		}else if(train->sens==ouestEst && train->type==M){
			nbMAttTunnel++;
			pthread_cond_wait(&condMAttTunnel,&mtxTunnel);
			nbMAttTunnel--;
			nbMVoieGarage_OE--;
			nbTrainTunnel_OE++;
		}
		printf("%sLe %s %d entre dans le tunnel\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
		sleep(tpsTunnel);
		printf("%sLe %s %d sort du tunnel\n",getTabulationFromSens(train->sens),getStringFromType(train->type),train->num);
	pthread_mutex_unlock(&mtxTunnel);
}

/*****************************************************************************************************/
/*****************************>>  Fonction des thread pour la gestion  <<*****************************/
/*****************************************************************************************************/
void * fonc_Aig1(){
	while (1)
		aig1(); 
}
void * fonc_Aig2(){
	while (1)
		aig2();
}
void * fonc_Tunnel(){
	while (1)
		tunnel();
}

/************************************************************************************************/
/*****************************>>  Fonction Train Ouest ----> Est  <<*****************************/
/************************************************************************************************/

void * FoncOuestEst(void *data){
	Train *train = (struct Train*) data;
	int priorite = (int) train->type;
	if(priorite==TGV){
		nbTrainVoieC++;
		printf("#### Le TGV %d arrive à la gare voie C ####\n",train->num);
		sleep(tpsGare);
		if(nbTgvTotal_OE==0){
			nbTgvAttSensVoieC++;
			P(semSensTGV);
			nbTgvAttSensVoieC--;
		}
		P(semTGV);
		nbTgvTotal_OE++;
		printf("Le TGV %d veut quitter la gare\n",train->num);
		passage_Aig2(train);
	}else if(priorite==GL){
		nbTrainVoieC++;
		printf("#### Le GL %d arrive à la gare voie C ####\n",train->num);
		sleep(tpsGare);
		if(nbGlTotal_OE==0){
			nbGlAttSensVoieC++;
			P(semSensGL);
			nbGlAttSensVoieC--;
		}
		nbGlTotal_OE++;
		P(semGL);
		printf("Le GL %d veut quitter la gare\n",train->num);
		passage_Aig2(train);
	}else if (priorite==M){
		pthread_mutex_lock(&mtxVoieA);
		printf("#### Le M %d arrive sur la voie A ####\n",train->num);
		sleep(tpsGare);
		nbTrainAttVoieA++;
		printf("Le M %d veut quitter la voie A\n",train->num);
		passage_Aig1(train);
		pthread_mutex_unlock(&mtxVoieA);
	}
	if(priorite==TGV){
		nbTrainAig2_OE--;
		printf("Le TGV %d entre sur la voie de garage TGV\n",train->num);
		sleep(tpsGarage);
		nbTgvVoieGarage_OE++;
	}else if(priorite==GL){
		nbTrainAig2_OE--;
		printf("Le GL %d entre sur la voie de garage GL\n",train->num);
		sleep(tpsGarage);
		nbGlVoieGarage_OE++;
	}else if(priorite==M){
		nbTrainAig1_OE--;
		printf("Le M %d entre sur la voie de garage M OuestEst\n",train->num);
		sleep(tpsGarage);
		nbMVoieGarage_OE++;
	}
	if(nbTrainLigne_OE==0){
		nbTrainAttSensLigneVoieGarage++;
		P(semSensLigne);		//semSensLigne Ouest->Est
		nbTrainAttSensLigneVoieGarage--;
	}
	passage_Tunnel(train);
	nbTrainTunnel_OE--;
	P(semLigne);
	nbTrainLigne_OE++;
	printf("Le %s %d entre sur la ligne\n",getStringFromType(train->type),train->num);
	if(priorite==TGV){
		nbTgvTotal_OE--;
		V(semTGV);
	}
	if(priorite==TGV && nbTgvTotal_OE==0){
		V(semSensTGV);
		for(j=0;i<nbTgvAttSensLigne;i++)
			V(semSensTGV);
	}
	if(priorite==GL){
		nbGlTotal_OE--;
		V(semGL);
	}
	if(priorite==GL && nbGlTotal_OE==0){
		V(semSensGL);
		for(k=0;k<nbGlAttSensLigne;k++)
			V(semSensGL);
	}
	sleep(tpsLigne);
	printf("***** Le %s %d sort de la ligne *****\n",getStringFromType(train->type),train->num);
	nbTrainLigne_OE--;
	V(semLigne);

	if(nbTrainLigne_OE==0){
		V(semSensLigne);
		for(l=0;l<nbTrainAttSensLigneLigne;l++)
			V(semSensLigne);
	}
}

/************************************************************************************************/
/*****************************>>  Fonction Train Est ----> Ouest  <<*****************************/
/************************************************************************************************/
void * FoncEstOuest(void *data){
	Train *train = (struct Train*) data;
	int priorite = (int) train->type;
	nbTrainAttTunnel++;
	printf("%s#### Le %s %d veut entrer sur la ligne ####\n",tabulation,getStringFromType(train->type),train->num);
	if(nbTrainLigne_EO==0){
		nbTrainAttSensLigneLigne++; 
		P(semSensLigne);		//semSensLigne Est->Ouest
		nbTrainAttSensLigneLigne--;
	}
	if(priorite==TGV && nbTgvTotal_EO==0){
		nbTgvAttSensLigne++;
		P(semSensTGV);			//sem Sens TGV
		nbTgvAttSensLigne--;
	}
	if(priorite==TGV){
		P(semTGV);
		nbTgvTotal_EO++;
	}
	if(priorite==GL && nbGlTotal_EO==0){
		nbGlAttSensLigne++;
		P(semSensGL);			//sem Sens GL
		nbGlAttSensLigne--;
	}
	if(priorite==GL){
		P(semGL);
		nbGlTotal_EO++;
	}
	P(semLigne);
	nbTrainLigne_EO++;
	printf("%sLe %s %d est sur la ligne\n",tabulation,getStringFromType(train->type),train->num);
	sleep(tpsLigne);
	nbTrainLigne_EO--;
	V(semLigne);
	passage_Tunnel(train);
	if(nbTrainLigne_EO==0){
		V(semSensLigne);
		for(m=0;m<nbTrainAttSensLigneVoieGarage;m++)
			V(semSensLigne);
	}
	if(priorite==TGV){
		printf("%sLe TGV %d est sur la voie de garage TGV\n",tabulation,train->num);
		nbTgvVoieGarage_EO++;
		sleep(tpsGarage);
		printf("%sLe TGV %d quitte la voie de garage TGV\n",tabulation,train->num);
		passage_Aig2(train);
		nbTrainAig2_EO--;
		nbTgvTotal_EO--;
		V(semTGV);
		if(nbTgvTotal_EO==0 && nbTgvAttSensVoieC>0){
			V(semSensTGV);
			for(n=0;n<nbTgvAttSensVoieC;n++)
				V(semSensTGV);
		}
		printf("%sLe TGV %d entre en gare voie D\n",tabulation,train->num);
		sleep(tpsGare);
		printf("%s***** Le TGV %d quitte la voie D *****\n",tabulation,train->num);
	}
	else if(priorite==GL){
		nbGlVoieGarage_EO++;
		printf("%sLe GL %d est sur la voie de garage GL\n",tabulation,train->num);
		sleep(tpsGarage);
		passage_Aig2(train);
		nbTrainAig2_EO--;
		nbGlTotal_EO--;
		V(semGL);
		if(nbGlTotal_EO==0){
			V(semSensGL);
			for(i=0;i<nbGlAttSensVoieC;i++)
				V(semSensGL);
		}
		printf("%sLe GL %d entre en gare voie D\n",tabulation,train->num);
		sleep(tpsGare);
		printf("%s***** Le GL %d quitte la voie D *****\n",tabulation,train->num);
	}
	else if(priorite==M){
		nbMVoieGarage_EO++;
		printf("%sLe M %d est sur la voie de garage M EstOuest\n",tabulation,train->num);
		sleep(tpsGarage);
		passage_Aig1(train);
		nbTrainAig1_EO--;
		printf("%sLe M %d entre sur la voie B\n",tabulation,train->num);
		sleep(tpsGare);
		printf("%s***** Le M %d quitte la voie B *****\n",tabulation,train->num);
	}
}

/************************************************************************************************/
/**********************>>  Lancement des trains dans la bonne direction  <<**********************/
/************************************************************************************************/

void * MiseSurVoie(void *data){
	Train *train = (struct Train*) data;
	int sens = (int) train->sens;	
	if(sens == 0) FoncOuestEst(train);
	else if(sens == 1) FoncEstOuest(train);
}

/************************************************************************************************/
/***********************************>>  Programme principal  <<**********************************/
/************************************************************************************************/

int main(){
	initialisation();
	printf("\n Gestion d'un système ferroviaire : \n");
	printf("Ouest vers Est");
	printf("\t \t \t Est vers Ouest\n");	
	departManuel();
	liberationRessources();
	//verificationDesCompteurs();
	printf("\n\n");
	printf("Projet réalisé par : \n Safwan Ghamrawi \n Etienne Trimaille \n\n");
	exit(0);
}

/************************************************************************************************/
/**************************************>>  Fonction divers  <<***********************************/
/************************************************************************************************/

void constructionTrain(Train* train, Type type, Sens sens, int num){
	train->type = type;
	train->sens = sens;
	train->num = num;
}

void departManuel(){

	int nbTrain = 28; //Définir le nombre de train que l'on va créer
	int i=0;	
	Train tabTrain[nbTrain];

	constructionTrain(&tabTrain[0],M,ouestEst,1);
	constructionTrain(&tabTrain[1],M,ouestEst,2);
	constructionTrain(&tabTrain[2],M,estOuest,3);
	constructionTrain(&tabTrain[3],M,estOuest,4);
	constructionTrain(&tabTrain[4],TGV,estOuest,5);
	constructionTrain(&tabTrain[5],TGV,estOuest,6);
	constructionTrain(&tabTrain[6],TGV,ouestEst,7);
	constructionTrain(&tabTrain[7],TGV,ouestEst,8);
	constructionTrain(&tabTrain[8],GL,ouestEst,9);
	constructionTrain(&tabTrain[9],GL,ouestEst,10);
	constructionTrain(&tabTrain[10],GL,estOuest,11);
	constructionTrain(&tabTrain[11],GL,estOuest,12);
	
	constructionTrain(&tabTrain[12],M,ouestEst,13);
	constructionTrain(&tabTrain[13],M,ouestEst,14);
	constructionTrain(&tabTrain[14],TGV,ouestEst,15);
	constructionTrain(&tabTrain[15],GL,estOuest,16);
	constructionTrain(&tabTrain[16],TGV,ouestEst,17);
	constructionTrain(&tabTrain[17],TGV,ouestEst,18);
	constructionTrain(&tabTrain[18],TGV,estOuest,19);
	constructionTrain(&tabTrain[19],TGV,estOuest,20);
	constructionTrain(&tabTrain[20],TGV,estOuest,21);
	constructionTrain(&tabTrain[21],GL,ouestEst,22);
	constructionTrain(&tabTrain[22],GL,ouestEst,23);
	constructionTrain(&tabTrain[23],GL,estOuest,24);
	constructionTrain(&tabTrain[24],M,estOuest,25);
	constructionTrain(&tabTrain[25],M,estOuest,26);
	constructionTrain(&tabTrain[26],M,ouestEst,27);
	constructionTrain(&tabTrain[27],M,ouestEst,28);
	
	for(i=0;i<nbTrain;i++){
		pthread_create(&(tabTrain[i].thread), NULL,(void *(*)())MiseSurVoie, (void*) &tabTrain[i]);
		sleep(tpsEntreTrains);	
	}

	for(i=0;i<nbTrain;i ++)
        	pthread_join(tabTrain[i].thread,NULL);
}

void verificationDesCompteurs(){
	printf("nbTrainAttVoieA= %d\nnbTrainVoieC= %d\nnbTrainAig1_OE= %d\n",nbTrainAttVoieA,nbTrainVoieC,nbTrainAig1_OE);

	printf("nbTrainAig1_EO= %d\nnbTrainAig2_OE= %d\nnbTrainAig2_EO= %d\n",nbTrainAig1_EO,nbTrainAig2_OE,nbTrainAig2_EO);

	printf("nbTgvVoieGarage_OE= %d\nnbTgvVoieGarage_EO= %d\nnbGlVoieGarage_OE= %d\n",nbTgvVoieGarage_OE,nbTgvVoieGarage_EO,nbGlVoieGarage_OE);
	
	printf("nbGlVoieGarage_EO= %d\nnbMVoieGarage_OE= %d\nnbMVoieGarage_EO= %d\n",nbGlVoieGarage_EO,nbMVoieGarage_OE,nbMVoieGarage_EO);

	printf("nbTrainTunnel_OE= %d\nnbTrainTunnel_EO= %d\nnbTrainAttTunnel= %d\n",nbTrainTunnel_OE,nbTrainTunnel_EO,nbTrainAttTunnel);
}

void initialisation(){
	//Initialisation des sémaphores
	initSem(semTGV, 3);
	initSem(semSensTGV, 1);
	initSem(semGL,3);
	initSem(semSensGL,1);
	initSem(semLigne,3);
	initSem(semSensLigne,1);

	//Initialisation des mutex
	pthread_mutex_init(&mtxAig1,0);
	pthread_mutex_init(&mtxAig2,0);
	pthread_mutex_init(&mtxTunnel,0);
	pthread_mutex_init(&mtxVoieA,0);

	//Initialisation des conditions
	pthread_cond_init(&condTrainAttAig2,0);
	pthread_cond_init(&condTrainAttLigne,0);
	pthread_cond_init(&condTrainAttVoieA,0);
	pthread_cond_init(&condTgvAttTunnel,0);
	pthread_cond_init(&condTgvAttAig2,0);
	pthread_cond_init(&condGlAttTunnel,0);
	pthread_cond_init(&condGlAttAig2,0);
	pthread_cond_init(&condMAttTunnel,0);
	pthread_cond_init(&condMAttAig1,0);
	pthread_cond_init(&condDormirAig1,0);
	pthread_cond_init(&condDormirAig2,0);
	pthread_cond_init(&condDormirTunnel,0);

	// creation des threads aig2,aig1,tunnel
	pthread_t thAig1;
	pthread_t thAig2;
	pthread_t thTunnel;
	
	pthread_create(&thAig1,0,(void *(*)())fonc_Aig1,NULL);
	pthread_create(&thAig2,0,(void *(*)())fonc_Aig2,NULL);
	pthread_create(&thTunnel,0,(void *(*)())fonc_Tunnel,NULL);
}

void liberationRessources(){
	pthread_mutex_destroy(&mtxAig1);
	pthread_mutex_destroy(&mtxAig2);
	pthread_mutex_destroy(&mtxTunnel);
	pthread_mutex_destroy(&mtxVoieA);
	
	pthread_cond_destroy(&condTrainAttAig2);
	pthread_cond_destroy(&condTrainAttLigne);
	pthread_cond_destroy(&condTrainAttVoieA);
	pthread_cond_destroy(&condTgvAttTunnel);
	pthread_cond_destroy(&condTgvAttAig2);
	pthread_cond_destroy(&condGlAttTunnel);
	pthread_cond_destroy(&condGlAttAig2);
	pthread_cond_destroy(&condMAttTunnel);
	pthread_cond_destroy(&condMAttAig1);
}
